/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin		 	*/
/*	Project:	GlbLib-1.0.0			*/
/*	Author:		gong_libin			*/
/*	Date:		2016_06_01			*/
/*	File:		GlbRaw.cpp			*/
/********************************************************/

#include "GlbRaw.h"

namespace GlbNet
{

CGlbRaw::CGlbRaw()
{
	memset(&m_stAddr, '\0', sizeof(struct sockaddr_in));
}

CGlbRaw::~CGlbRaw()
{
}

void* CGlbRaw::GlbRawGetAddr()
{
	return (void*)&m_stAddr;
}

void CGlbRaw::GlbRawReset()
{
	memset(&m_stAddr, '\0', sizeof(struct sockaddr_in));

	return;
}

void CGlbRaw::GlbRawSetAddr(void* pAddr)
{
	memcpy(&m_stAddr, pAddr, sizeof(struct sockaddr_in));

	return;
}

void CGlbRaw::GlbRawSetAddr(char* pszUrl)
{
	char* pszPort = NULL;

	if (NULL != (pszPort = CGlbNet::GlbNetUrl(pszUrl))) {
		m_stAddr.sin_family = AF_INET;
		m_stAddr.sin_port = htons(atoi(pszPort));
		m_stAddr.sin_addr.s_addr = inet_addr(pszUrl);
	}

	return;
}

void CGlbRaw::GlbRawSetAddr(char* pszAddr, USHORT usPort)
{
	m_stAddr.sin_family = AF_INET;
	m_stAddr.sin_port = htons(usPort);
	m_stAddr.sin_addr.s_addr = inet_addr(pszAddr);

	return;
}

int CGlbRaw::GlbRawPromisc(char* pszDev, short sFlag)
{
	struct ifreq stIfreq;
 
	stIfreq.ifr_flags = sFlag;
	strcpy(stIfreq.ifr_name, pszDev);

	return ioctl(m_iSocket, SIOCSIFFLAGS, &stIfreq);
}

int CGlbRaw::GlbRawInitSend(int iDomain, int iType, int iProto)
{
	int iOn = 1;

	if ((m_iSocket = socket(iDomain, iType, iProto)) > 0) {
		setsockopt(m_iSocket, IPPROTO_IP, IP_HDRINCL, (char*)&iOn, sizeof(iOn));
	}
	else {
		GLB_ERROR("%s\n", strerror(errno));
		return GLB_FAILURE;
	}

	return GLB_SUCCESS;
}

int CGlbRaw::GlbRawInitRecv(int iDomain, int iType, int iProto)
{
	if (GLB_FAILURE == (m_iSocket = socket(iDomain, iType, iProto))) {
		GLB_ERROR("%s\n", strerror(errno));
		return GLB_FAILURE;
	}

	return GLB_SUCCESS;
}

void CGlbRaw::GlbRawIPHead(UCHAR* puszIP, UCHAR ucPro, struct in_addr stSrc, struct in_addr stDst, USHORT usLength)
{
	struct ip* pstIP = (struct ip*)puszIP;

	pstIP->ip_v = 4;
	pstIP->ip_hl = 5;
	pstIP->ip_ttl = 60;
	pstIP->ip_p = ucPro;
	pstIP->ip_id = htons(getpid());
	pstIP->ip_len = htons(usLength);

	pstIP->ip_src = stSrc;
	pstIP->ip_dst = stDst;

	pstIP->ip_sum = (USHORT)GlbRawIPCheck((USHORT*)pstIP, sizeof(struct ip));

	return;
}

USHORT CGlbRaw::GlbRawIPCheck(USHORT* pusIP, int iLength)
{
	USHORT usAnswer = 0;
	register int iSum = 0;
	register int iLeft = iLength;
	register USHORT* pusWord = pusIP;

	while (iLeft > 1) {
		iSum += *pusWord ++;
		iLeft -= 2;
	}

	if (1 == iLeft) {
		*(UCHAR*)&usAnswer = *(UCHAR*)pusWord;
		iSum += usAnswer;
	}

	iSum = (iSum >> 16) + (iSum & 0xffff);
	iSum += (iSum >> 16);
	usAnswer = ~iSum;

	return usAnswer;
}

void CGlbRaw::GlbRawTcpHead(UCHAR* puszTcp, USHORT usSrc, USHORT usDst, ULONG ulSeq, ULONG ulAck)
{
	struct tcphdr* pstTcp = (struct tcphdr*)puszTcp;

	pstTcp->th_x2 = 0;
	pstTcp->th_off = 5;

	pstTcp->th_seq = htonl(ulSeq);
	pstTcp->th_ack = htonl(ulAck);

	pstTcp->th_sport = htons(usSrc);
	pstTcp->th_dport = htons(usDst);

	pstTcp->th_win = htons(0xffff);
	pstTcp->th_flags = TH_ACK | TH_PUSH | TH_FIN;

	return;
}

void CGlbRaw::GlbRawUdpHead(UCHAR* puszUdp, USHORT usSrc, USHORT usDst, int iLength)
{
	struct udphdr* pstUdp = (struct udphdr*)puszUdp;

	pstUdp->uh_sum = 0;
	pstUdp->uh_sport = htons(usSrc);
	pstUdp->uh_dport = htons(usDst);
	pstUdp->uh_ulen = htons(iLength);

	return;
}

USHORT CGlbRaw::GlbRawPortCheck(UCHAR ucPro, UCHAR* puszTcp, int iLength, struct in_addr stSrc, struct in_addr stDst)
{
	char szGlbeudo[GLB_KBYTES2] = { 0 };
	struct {
		struct in_addr m_stSrc;
		struct in_addr m_stDst;
		UCHAR m_ucHolder;
		UCHAR m_ucProtocol;
		USHORT m_usLength;
	} stGlbeudo;

	memset(&stGlbeudo, '\0', sizeof(stGlbeudo));

	stGlbeudo.m_stSrc = stSrc;
	stGlbeudo.m_stDst = stDst;
	stGlbeudo.m_ucHolder = 0;
	stGlbeudo.m_ucProtocol = ucPro;
	stGlbeudo.m_usLength = htons(iLength);

	memcpy(szGlbeudo, &stGlbeudo, sizeof(stGlbeudo));
	memcpy(szGlbeudo + sizeof(stGlbeudo), puszTcp, iLength);

	return (USHORT)GlbRawIPCheck((USHORT*)szGlbeudo, sizeof(stGlbeudo) + iLength);
}

void CGlbRaw::GlbRawTcpReset(UCHAR* puszHead, UCHAR* puszDst, int* piDst)
{
	ULONG ulSeq = 0;
	ULONG ulAck = 0;
	USHORT usIP = 0;
	USHORT usSrc = 0;
	USHORT usDst = 0;
	USHORT usIPTCP = 0;
	struct in_addr stSrc;
	struct in_addr stDst;
	struct tcphdr* pstTcp = NULL;
	struct tcphdr* pstReset = NULL;
	struct ip* pstIP = (struct ip*)puszHead;
	UCHAR uszReset[sizeof(struct ip) + sizeof(struct tcphdr)] = { 0 };

	usIP = ntohs(pstIP->ip_len);
	memcpy(&stSrc, &pstIP->ip_src, sizeof(struct in_addr));
	memcpy(&stDst, &pstIP->ip_dst, sizeof(struct in_addr));

	pstTcp = (struct tcphdr*)(puszHead + pstIP->ip_hl * 4);
	usIPTCP = pstIP->ip_hl * 4 + pstTcp->th_off * 4;
	usSrc = ntohs(pstTcp->th_sport);
	usDst = ntohs(pstTcp->th_dport);
	ulSeq = ntohl(pstTcp->th_seq);
	ulAck = ntohl(pstTcp->th_ack);
	*piDst = sizeof(uszReset);

	pstReset = (struct tcphdr*)(uszReset + sizeof(struct ip));
	GlbRawTcpHead((UCHAR*)pstReset, usSrc, usDst, ulSeq + usIP - usIPTCP, ulSeq + usIP - usIPTCP);

	pstReset->th_win = 0;
	pstReset->th_flags = TH_RST;
	pstReset->th_sum = GlbRawPortCheck(GLB_IPPROTO_TCP, (UCHAR*)pstReset, sizeof(uszReset) - sizeof(struct ip), stSrc, stDst);

	GlbRawIPHead(uszReset, GLB_IPPROTO_TCP, stSrc, stDst, *piDst);

	memcpy(puszDst, uszReset, *piDst);

	return;
}

void CGlbRaw::GlbRawTcpRedir(UCHAR* puszHead, char* pszSrc, int iSrc, UCHAR* puszDst, int* piDst)
{
	ULONG ulSeq = 0;
	ULONG ulAck = 0;
	USHORT usIP = 0;
	USHORT usSrc = 0;
	USHORT usDst = 0;
	USHORT usIPTCP = 0;
	struct in_addr stSrc;
	struct in_addr stDst;
	struct tcphdr* pstTcp = NULL;
	struct tcphdr* pstRedir = NULL;
	struct ip* pstIP = (struct ip*)puszHead;
	UCHAR uszRedir[sizeof(struct ip) + sizeof(struct tcphdr) + GLB_KBYTES2] = { 0 };

	usIP = ntohs(pstIP->ip_len);
	memcpy(&stSrc, &pstIP->ip_src, sizeof(struct in_addr));
	memcpy(&stDst, &pstIP->ip_dst, sizeof(struct in_addr));

	pstTcp = (struct tcphdr*)(puszHead + pstIP->ip_hl * 4);
	usIPTCP = pstIP->ip_hl * 4 + pstTcp->th_off * 4;
	usSrc = ntohs(pstTcp->th_sport);
	usDst = ntohs(pstTcp->th_dport);
	ulSeq = ntohl(pstTcp->th_seq);
	ulAck = ntohl(pstTcp->th_ack);
	*piDst = usIPTCP + iSrc;

	memcpy(uszRedir + usIPTCP, pszSrc, iSrc);
	pstRedir = (struct tcphdr*)(uszRedir + sizeof(struct ip));
	GlbRawTcpHead((UCHAR*)pstRedir, usDst, usSrc, ulAck,  ulSeq + usIP - usIPTCP);

	pstRedir->th_off = pstTcp->th_off;
	pstRedir->th_sum = GlbRawPortCheck(GLB_IPPROTO_TCP, (UCHAR*)pstRedir,  *piDst - sizeof(struct ip), stDst, stSrc);

	GlbRawIPHead(uszRedir, GLB_IPPROTO_TCP, stDst, stSrc, *piDst);

	memcpy(puszDst, uszRedir, *piDst);

	return;
}

void CGlbRaw::GlbRawUdpRedir(UCHAR* puszHead, char* pszSrc, int iSrc, UCHAR* puszDst, int* piDst)
{
	USHORT usSrc = 0;
	USHORT usDst = 0;
	struct in_addr stSrc;
	struct in_addr stDst;
	struct udphdr* pstUdp = NULL;
	struct udphdr* pstRedir = NULL;
	struct ip* pstIP = (struct ip*)puszHead;
	UCHAR uszRedir[sizeof(struct ip) + sizeof(struct udphdr) + GLB_KBYTES2] = { 0 };

	memcpy(&stSrc, &pstIP->ip_src, sizeof(struct in_addr));
	memcpy(&stDst, &pstIP->ip_dst, sizeof(struct in_addr));

	pstUdp = (struct udphdr*)(puszHead + pstIP->ip_hl * 4);
	usSrc = ntohs(pstUdp->uh_sport);
	usDst = ntohs(pstUdp->uh_dport);

	*piDst = sizeof(struct ip) + sizeof(struct udphdr) + iSrc;
	memcpy(uszRedir + sizeof(struct ip) + sizeof(struct udphdr), pszSrc, iSrc);

	pstRedir = (struct udphdr*)(uszRedir + sizeof(struct ip));
	GlbRawUdpHead((UCHAR*)pstRedir, usDst, usSrc, sizeof(struct udphdr) + iSrc);
	pstRedir->uh_sum = GlbRawPortCheck(IPPROTO_UDP, (UCHAR*)pstRedir, sizeof(struct udphdr) + iSrc, stDst, stSrc);

	GlbRawIPHead(uszRedir, GLB_IPPROTO_UDP, stDst, stSrc, sizeof(struct ip) + sizeof(struct udphdr) + iSrc);

	memcpy(puszDst, uszRedir, *piDst);

	return;
}

} /* GlbNet */
